package com.saceem.imagenes.test;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;

import javax.ejb.Stateless;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.stream.ImageOutputStream;
import javax.media.jai.JAI;

import org.apache.log4j.Logger;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

import com.sun.media.imageio.plugins.tiff.TIFFImageWriteParam;
import com.sun.media.jai.codec.FileSeekableStream;
import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageDecoder;
import com.sun.media.jai.codec.SeekableStream;
import com.sun.media.jai.codec.TIFFDecodeParam;
import com.sun.media.jai.codec.TIFFEncodeParam;

@Stateless
public class FuncionesImagenes {

	private final static Logger log = Logger.getLogger(FuncionesImagenes.class
			.getName());

	public static int getResolucionEscaneoImagen(String rutaImagen,
			String formato) throws FuncionesImagenesException {
		Iterator<ImageReader> readers = ImageIO
				.getImageReadersByFormatName(formato);
		ImageReader reader = (ImageReader) readers.next();
		ImageInputStream iis = null;
		try {
			iis = ImageIO.createImageInputStream(new File(rutaImagen));
			reader.setInput(iis, true);

			IIOMetadata tags = reader.getImageMetadata(0);
			int i = getResolucionEscaneo(tags);
			return i;
		} catch (Exception e) {
			throw new FuncionesImagenesException(
					"No se pudo cargar la imagen. Verifique que tenga instalado JAI!.",
					e);
		} finally {
			try {
				if (iis != null)
					iis.close();
			} catch (Exception localException2) {
			}
		}
		// throw localObject;
	}

	public static BufferedImage cargarImagenDeArchivo(String rutaImagen,
			String formato, int pagina) throws FuncionesImagenesException {
		BufferedImage data = null;
		Iterator<ImageReader> readers = ImageIO
				.getImageReadersByFormatName(formato);
		ImageReader reader = (ImageReader) readers.next();
		ImageInputStream iis = null;
		try {
			iis = ImageIO.createImageInputStream(new File(rutaImagen));
			reader.setInput(iis, true);
			data = reader.read(pagina);
			BufferedImage localBufferedImage1 = data;
			return localBufferedImage1;
		} catch (Exception e) {
			throw new FuncionesImagenesException(
					"No se pudo cargar la imagen. Verifique que tenga instalado JAI!.",
					e);
		} finally {
			try {
				if (iis != null)
					iis.close();
			} catch (Exception localException2) {
			}
		}
		// throw localObject;
	}

	public static void guardarImagen(BufferedImage imagen, String rutaImagen,
			String formato) throws FuncionesImagenesException {
		Iterator<ImageWriter> writers = ImageIO
				.getImageWritersByFormatName(formato);
		ImageWriter writer = (ImageWriter) writers.next();

		Iterator<ImageReader> readers = ImageIO
				.getImageReadersByFormatName(formato);
		ImageReader reader = (ImageReader) readers.next();
		ImageOutputStream ios = null;
		try {
			IIOMetadata tags = null;
			ImageInputStream iis = null;
			try {
				iis = ImageIO.createImageInputStream(new File(rutaImagen));
				reader.setInput(iis, true);

				tags = reader.getImageMetadata(0);
				log.info("la resolucion es : " + getResolucionEscaneo(tags));
			} catch (Throwable localThrowable) {
				try {
					iis.close();
				} catch (Exception localException1) {
				}
			} finally {
				try {
					iis.close();
				} catch (Exception localException2) {
				}
			}

			ios = ImageIO.createImageOutputStream(new File(rutaImagen));
			writer.setOutput(ios);
			TIFFImageWriteParam writeParam = (TIFFImageWriteParam) writer
					.getDefaultWriteParam();
			writeParam.setCompressionMode(2);
			writeParam.setCompressionType("CCITT RLE");
			IIOImage imangeiio = new IIOImage(imagen, null, tags);
			writer.write(imangeiio);

			ios.close();
		} catch (Exception e) {
			e.printStackTrace();
			throw new FuncionesImagenesException(
					"No se pudo guardar la imagen. Verifique que tenga instalado JAI!.",
					e);
		} finally {
			try {
				if (ios != null)
					ios.close();
			} catch (Exception localException4) {
			}
		}
	}

	public static void guardarImagen8Bits(BufferedImage imagen,
			String rutaImagen, String formato, int tipoCompresion)
			throws FuncionesImagenesException {
		TIFFEncodeParam params = new TIFFEncodeParam();
		params.setCompression(tipoCompresion);
		FileOutputStream os = null;
		try {
			os = new FileOutputStream(rutaImagen);
			JAI.create("encode", imagen, os, "TIFF", params);
		} catch (FileNotFoundException fnfe) {
			throw new FuncionesImagenesException(fnfe.getMessage(), fnfe);
		} finally {
			if (os != null)
				try {
					os.close();
				} catch (Exception localException) {
				}
		}
	}

	private static int getResolucionEscaneo(IIOMetadata meta) {
		Node n = meta.getAsTree("javax_imageio_1.0");
		n = n.getFirstChild();
		int xDPI = 0;
		int yDPI = 0;
		while (n != null) {
			if (n.getNodeName().equals("Dimension")) {
				Node n2 = n.getFirstChild();
				while (n2 != null) {
					if (n2.getNodeName().equals("HorizontalPixelSize")) {
						NamedNodeMap nnm = n2.getAttributes();
						Node n3 = nnm.item(0);
						float hps = Float.parseFloat(n3.getNodeValue());
						xDPI = Math.round(25.4F / hps);
					}
					if (n2.getNodeName().equals("VerticalPixelSize")) {
						NamedNodeMap nnm = n2.getAttributes();
						Node n3 = nnm.item(0);
						float vps = Float.parseFloat(n3.getNodeValue());
						yDPI = Math.round(25.4F / vps);
					}
					n2 = n2.getNextSibling();
				}
			}
			n = n.getNextSibling();
		}
		if (xDPI != 0) {
			return xDPI;
		}
		return yDPI;
	}

	public static void copiarImagen(String origen, String destino)
			throws FuncionesImagenesException {
		try {
			File f1 = new File(origen);
			File f2 = new File(destino);
			InputStream in = new FileInputStream(f1);

			OutputStream out = new FileOutputStream(f2);

			byte[] buf = new byte[1024];
			int len;
			while ((len = in.read(buf)) > 0) {
				// int len;
				out.write(buf, 0, len);
			}
			in.close();
			out.close();
		} catch (FileNotFoundException ex) {
			throw new FuncionesImagenesException(
					"No se encontro la imagen o la carpeta origen o la carpeta destino.",
					ex);
		} catch (IOException e) {
			throw new FuncionesImagenesException(
					"Ocurrio una error durante la lectura o escritura.", e);
		}
	}

	public static void eliminarImagen(String ruta)
			throws FuncionesImagenesException {
		try {
			File f1 = new File(ruta);
			if (!f1.canWrite())
				throw new FuncionesImagenesException(
						"No se puede borrar el archivo, no tiene permisos");
			
			if (!f1.delete()) {
				log.info("No se borro la imagen");
				try {
					Runtime.getRuntime().exec("cmd /c del \"" + ruta + "\"");
				} catch (Exception err) {
					err.printStackTrace();
				}
			}
		} catch (Throwable t) {
			throw new FuncionesImagenesException(t.getMessage());
		}
	}

	public static void moverImagen(String origen, String destino)
			throws FuncionesImagenesException {
		copiarImagen(origen, destino);
		eliminarImagen(origen);
	}

	public static BufferedImage rotarImagenDerecha(BufferedImage imagen) {
		return rotarImagen2(imagen, 90);
	}

	public static BufferedImage rotarImagenIzquierda(BufferedImage imagen) {
		return rotarImagen2(imagen, 270);
	}

	public static BufferedImage rotarImagen2(BufferedImage image, int grados) {
		int w = image.getWidth();
		int h = image.getHeight();

		BufferedImage image2 = new BufferedImage(image.getHeight(),
				image.getWidth(), 12);

		Graphics2D g2d = image2.createGraphics();
		double x = (h - w) / 2.0D;
		double y = (w - h) / 2.0D;

		AffineTransform at = AffineTransform.getTranslateInstance(x, y);
		at.rotate(Math.toRadians(grados), w / 2.0D, h / 2.0D);

		g2d.drawRenderedImage(image, at);

		return image2;
	}

	public static BufferedImage rotarImagen(BufferedImage inputImage, int grados) {
		try {
			BufferedImage sourceBI = new BufferedImage(
					inputImage.getWidth(null), inputImage.getHeight(null), 12);

			AffineTransform at = new AffineTransform();

			at.scale(1.0D, 1.0D);

			at.rotate(grados * 3.141592653589793D / 180.0D,
					sourceBI.getWidth() / 2.0D, sourceBI.getHeight() / 2.0D);

			AffineTransform translationTransform = findTranslation(at,
					sourceBI, grados);

			at.preConcatenate(translationTransform);

			BufferedImageOp bio = new AffineTransformOp(at, 2);

			BufferedImage destinationBI = new BufferedImage(
					inputImage.getHeight(null), inputImage.getWidth(null), 12);
			bio.filter(sourceBI, destinationBI);

			return destinationBI;
		} catch (Throwable t) {
			t.printStackTrace();
		}
		return inputImage;
	}

	private static AffineTransform findTranslation(AffineTransform at,
			BufferedImage bi, int grados) {
		double ytrans = 0.0D;
		double xtrans = 0.0D;

		if ((grados != 270) && (grados != 180)) {
			Point2D p2din = new Point2D.Double(0.0D, 0.0D);
			Point2D p2dout = at.transform(p2din, null);
			ytrans = p2dout.getY();

			p2din = new Point2D.Double(0.0D, bi.getHeight());
			p2dout = at.transform(p2din, null);
			xtrans = p2dout.getX();
		} else if (grados != 180) {
			Point2D p2din = new Point2D.Double(bi.getWidth(), 0.0D);
			Point2D p2dout = at.transform(p2din, null);
			ytrans = p2dout.getY();

			p2din = new Point2D.Double(0.0D, 0.0D);
			p2dout = at.transform(p2din, null);
			xtrans = p2dout.getX();
		}

		AffineTransform tat = new AffineTransform();
		tat.translate(-xtrans, -ytrans);
		return tat;
	}

	public static BufferedImage desplazarImagen(BufferedImage imagen, int x,
			int y) {
		AffineTransform tat = new AffineTransform();
		tat.translate(x, y);

		BufferedImageOp bio = new AffineTransformOp(tat, 2);

		BufferedImage resultado = new BufferedImage(imagen.getWidth(null),
				imagen.getHeight(null), 12);
		bio.filter(imagen, resultado);

		return resultado;
	}

	public static int contarPaginasImagen(String ruta, String formato)
			throws IOException {
		File file = new File(ruta);
		SeekableStream s = new FileSeekableStream(file);

		TIFFDecodeParam param = null;
		ImageDecoder dec = ImageCodec.createImageDecoder("tiff", s, param);

		int paginas = dec.getNumPages();
		s.close();

		return paginas;
	}

	public static void agregarMarcaDeAgua(String rutaImagen, String formato,
			int pagina, String texto, int offset_x, int offset_y)
			throws FuncionesImagenesException {
		Color color = Color.GRAY;
		Font font = new Font("serif", 0, 40);

		Image src = cargarImagenDeArchivo(rutaImagen, formato, pagina);
		int width = src.getWidth(null);
		int height = src.getHeight(null);
		BufferedImage image = new BufferedImage(width, height, 1);
		Graphics g = image.createGraphics();
		g.drawImage(src, 0, 0, width, height, null);
		g.setColor(color);
		g.setFont(font);
		g.drawString(texto, offset_x, height - font.getSize() / 2 - offset_y);
		g.dispose();

		guardarImagen(image, "c:\\marcadeagua.tif", formato);
	}

	public static void agregarMarcaDeAguaLogo(String rutaImagen,
			String formato, int pagina, String rutaImagenaAgregar,
			String rutaDestino) throws FuncionesImagenesException {
		Image src = cargarImagenDeArchivo(rutaImagen, formato, pagina);
		Image logo = cargarImagenDeArchivo(rutaImagenaAgregar, formato, pagina);

		int width = src.getWidth(null);
		int height = src.getHeight(null);

		int ancho = logo.getWidth(null);
		int alto = logo.getHeight(null);
		int ancho2 = 0;
		int alto2 = 0;
		if (new Integer(width).doubleValue()
				/ new Integer(height).doubleValue() > new Integer(ancho)
				.doubleValue() / new Integer(alto).doubleValue()) {
			alto2 = height;
			// ancho2 = new Double(alto2 * ancho / new
			// Integer(alto).doubleValue(), alto2).intValue();
		} else {
			ancho2 = width;
			// alto2 = new Double(ancho2 * alto / new
			// Integer(ancho).doubleValue(), ancho2).intValue();
		}

		BufferedImage image = new BufferedImage(width, height, 10);
		Graphics2D g = image.createGraphics();
		g.drawImage(src, 0, 0, width, height, null);

		AlphaComposite alpha = AlphaComposite.getInstance(3, 0.2F);
		g.setComposite(alpha);
		g.drawImage(logo, (width - ancho2) / 2, (height - alto2) / 2, ancho2,
				alto2, null);

		guardarImagen8Bits(image, rutaDestino, formato, 32946);
	}

	public static void agregarMarcaDeAguaDotNet(String rutaExe,
			String rutaImagen, String rutaImagenaAgregar, String rutaDestino)
			throws FuncionesImagenesException {
		try {
			Process p = Runtime
					.getRuntime()
					.exec(rutaExe + " marca_de_agua \"" + rutaImagen + "\" \""
							+ rutaImagenaAgregar + "\" \"" + rutaDestino + "\"");

			p.waitFor();
		} catch (IOException ioe) {
			throw new FuncionesImagenesException(ioe.getMessage(), ioe);
		} catch (InterruptedException ie) {
			throw new FuncionesImagenesException(ie.getMessage(), ie);
		}
	}

	public static BufferedImage getSubImagen(String rutaImagen, int x, int y,
			int w, int h) throws FuncionesImagenesException {
		// BufferedImage input = null;
		// if (rutaImagen.endsWith("tif")) {
		// input = cargarImagenDeArchivo(rutaImagen, "tif", 0);
		// } else {
		// input = cargarImagenDeArchivo(rutaImagen, "png", 0);
		// }

		String formato = rutaImagen.substring((rutaImagen.length() - 3));
		// log.info("Formato detectado : " + formato);
		BufferedImage input = cargarImagenDeArchivo(rutaImagen, "jpg", 0);
		BufferedImage output = input.getSubimage(x, y, w, h);

		return output;
	}

}
